`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////Asyncsys Lab////////////////////////////////////////
// Author: Zhenbang Kang
// Email: kangzhb20@lzu.edu.cn
// Create Date: 2022/06/10 
// Module Name: uart_unit
// Project Name: ac8051 (Asynchronous Click 8051)
// Target Devices: XC7A100TFGG484-2
// Tool Versions: Vivado 2019.1
// Description:UART Module
// Revision: V2.0
//////////////////////////////////////////////////////////////////////////////////
`include "D:/fpga_8051/code/fpga/rtl/include/DW8051_package.v"
`include "D:/fpga_8051/code/fpga/rtl/include/DW8051_parameter.v"

module uart_unit(
                fire_uart,
                fire_rx,
                in_R_rst,
                rst_n,                
                sys_clk,
                sfr_addr, 
                sfr_data_out, 
                rx_data,
                sfr_wr, 
                sfr_rd,
//                fire_out,
                fire_tx_end,
                uart_tx,
                uart_rx,
                fire_rx_end
    );
parameter                       CLK_FRE = 200;      //Mhz
  
    input               fire_uart;
    input               fire_rx;
    input               in_R_rst;
    input               rst_n;
    input               sys_clk;
    input   [7:0]       sfr_addr;
    input   [7:0]       sfr_data_out;
    input               sfr_wr;
    input               sfr_rd;
//    output  [7:0]       sfr_data_in;
    output              fire_tx_end;
//    output              fire_out;
    output              uart_tx;
    input               uart_rx;
    output   [7:0]      rx_data;
    output              fire_rx_end;
     
    wire                fire_uart;
    wire                fire_rx;
    wire                fire_req;
    wire                fire_start;
    wire                fire_still;
    wire                fire_still_inv;
    wire                fire_st2;
//    wire                fire_tx_begin;
    wire                fire_out;
    wire                fire_out_inv;
    wire                tx_end;
    wire                fire_tx_end;    
    
    wire                sys_clk;
    wire                rst_n;
    wire                in_R_rst;
 (* dont_touch = "true" *)reg   [7:0]         tx_data;
 (* dont_touch = "true" *)wire                uart_tx;
 wire                tx_data_valid;
 wire                tx_data_ready; 
 (* dont_touch = "true" *)reg                 cnt_tx_1;

 reg                 cnt_state;
 (* dont_touch = "true" *)wire                out_R_req;
 (* dont_touch = "true" *)wire                out_A_req;
 (* dont_touch = "true" *)wire                in_A_tx1; 
 (* dont_touch = "true" *)wire                in_A_ts1;
 (* dont_touch = "true" *)wire                out_R_ts1;
  (* dont_touch = "true" *)reg                out_A_ts1;
 (* dont_touch = "true" *)wire                in_A_tx_begin;
 (* dont_touch = "true" *)wire                out_R_tx_begin;
 (* dont_touch = "true" *)wire                out_A_tx_begin;
 (* dont_touch = "true" *)reg                in_R_tx_end;
 (* dont_touch = "true" *)wire                in_A_tx_end;
 (* dont_touch = "true" *)reg                out_A_tx_end;
 (* dont_touch = "true" *)wire                out_R_tx_end; 
   
    
 (* dont_touch = "true" *)wire  [7:0]         sfr_addr;
 (* dont_touch = "true" *)wire  [7:0]         sfr_data_out;
 wire                sfr_wr;
 wire                sfr_rd;
 
  wire   sfr_wr_vaild;
//  wire   tx_data_ready_valid;
//  wire   tx_begin;
  
  reg       tx_cnt;
  reg [5:0] sfr_cnt1;
  reg [5:0] sfr_cnt2;
  reg       tx_data_valid_begin;
  reg       tx_data_valid_end;
//    wire  [7:0]         sfr_data_in;

/*************************TX_PART************************/
always@(posedge fire_uart or negedge rst_n)begin
    if(!rst_n)begin
        cnt_tx_1 <= 'b0;
        cnt_state <= 'b0;
    end else begin
        cnt_state <= 1'b1;
        cnt_tx_1 <= ~cnt_tx_1;
        
    end
end
(* keep_hierarchy = "yes" *)click_start click_req(  
                                    .in_R(cnt_tx_1),      
//                                    .in_A(),    
                                    .out_R(out_R_req),   
                                    .out_A(in_A_ts1),   
                                    .fire_out(fire_start)    
//                                    .rst(rst_n)
                                    );
always@(posedge fire_start or negedge rst_n)begin
    if(!rst_n)begin
        tx_data <= 8'b0;
    end else if(sfr_addr == `uart_tx) begin
        tx_data <= sfr_data_out;
    end else begin
        tx_data <= tx_data; 
    end    
end

(* keep_hierarchy = "yes" *)click tx_stage1(  
                                    .in_R(out_R_req),      
                                    .in_A(in_A_ts1),    
                                    .out_R(out_R_ts1),   
                                    .out_A(out_A_ts1),   
                                    .fire(fire_still)    
//                                    .rst(rst_n)
                                    );
 assign fire_still_inv = ~fire_still;
 always@(posedge fire_still_inv or negedge in_R_rst)begin
    if(!in_R_rst)begin
        out_A_ts1 <= 'b0;
    end else begin
        out_A_ts1 <= out_R_ts1;
    end
 end                                                                        

assign sfr_wr_vaild = (sfr_addr == `uart_tx)? sfr_wr : 0;
//assign tx_data_ready_valid = (cnt_state == 1)? tx_data_ready : 0;
//assign tx_begin = sfr_wr_vaild & tx_data_ready_valid;

always@(posedge sys_clk or negedge rst_n)begin
    if(!rst_n)begin
        tx_cnt <='b0;
    end else if(sfr_wr_vaild==0)begin
        tx_cnt <='b0;
    end else if(sfr_cnt2==6'd20)begin
        tx_cnt <='b1;
    end else begin
        tx_cnt <= tx_cnt;
    end
end

always@(posedge sys_clk or negedge rst_n)begin
    if(!rst_n)begin
        sfr_cnt1 <= 'b0;
    end else if((sfr_wr_vaild==1)&&(tx_data_valid_begin==0)&&(tx_cnt==0))begin
        sfr_cnt1 <= sfr_cnt1+1;
    end else if(tx_data_valid_begin==1)begin
        sfr_cnt1 <='b0;
    end else begin
        sfr_cnt1 <= sfr_cnt1;
    end
end

always@(posedge sys_clk or negedge rst_n)begin
    if(!rst_n)begin
        tx_data_valid_begin <= 'b0;
    end else if(sfr_cnt1==6'd20)begin
        tx_data_valid_begin <= 'b1;
    end else if(sfr_cnt2==6'd20)begin
        tx_data_valid_begin <= 'b0;
    end else begin
        tx_data_valid_begin <= tx_data_valid_begin;
    end
end

always@(posedge sys_clk or negedge rst_n)begin
    if(!rst_n)begin
        sfr_cnt2 <= 'b0;
    end else if((sfr_wr_vaild==1)&&(tx_data_valid_begin==1)&&(tx_cnt==0))begin
        sfr_cnt2 <= sfr_cnt2+1;
    end else if(tx_data_valid_begin==0)begin
        sfr_cnt2 <='b0;
    end else begin
        sfr_cnt2 <= sfr_cnt2;
    end
end
assign tx_data_valid = tx_data_valid_begin;
//always@(posedge tx_begin or negedge rst_n)begin
//    if(!rst_n)begin
//        tx_data_valid <= 'b0;
//    end else begin
//        tx_data_valid <= ~tx_data_valid;        
//    end
//end

assign tx_end = (sfr_addr == `uart_tx) ? (tx_data_ready & cnt_state) : 0;

always@(posedge tx_end or negedge rst_n)begin
    if(!rst_n)begin
        in_R_tx_end <= 'b0;
    end else begin
        in_R_tx_end <= ~in_R_tx_end;
    end 
end

(* keep_hierarchy = "yes" *)tx_final tx_final(  
                                    .in_R(in_R_tx_end),   
                                    .in_A(in_A_tx_end),  
                                    .out_R(out_R_tx_end),   
                                    .out_A(out_A_tx_end),   
                                    .fire(fire_out));
                                    
//assign out_A_tx_end = out_R_tx_end;
assign fire_out_inv = ~fire_out;

always@(posedge fire_out_inv or negedge in_R_rst)begin
    if(!in_R_rst)begin
        out_A_tx_end <= 'b0;
    end else begin
        out_A_tx_end <= out_R_tx_end;
    end
end
assign fire_tx_end = (sfr_addr!=`uart_tx)? (fire_start&cnt_state) : (fire_out&cnt_state);
/********************************************************/    
    
(* keep_hierarchy = "yes" *)uart_tx#
(
.CLK_FRE(CLK_FRE),
.BAUD_RATE(115200)
//.BAUD_RATE(115200)
) uart_tx_inst
(
.clk                        (sys_clk                  ),
.rst_n                      (rst_n                    ),
.tx_data                    (tx_data                  ),
.tx_data_valid              (tx_data_valid            ),
.tx_data_ready              (tx_data_ready            ),
.tx_pin                     (uart_tx                  )
);

/*************************RX_Part***************************/
wire[7:0]                        rx_data;
wire                             rx_data_valid;    // receiving data valid
wire                             rx_data_ready;    // singal for receiving data
wire                             uart_rx_in;
reg                              rx_start;
reg                              out_A_rx;
wire                             out_R_rx;
wire                             fire_rx_begin;
wire                             inv_fire_rx_begin;
reg                               rx_final;
reg                              out_A_rxend;
wire                             out_R_rxend;
wire                             fire_rx_final;
wire                             inv_fire_rx_final;
wire                             fire_rx_end;
wire                             uart_rx;
assign rx_data_ready = 1'b1;
always@(posedge fire_rx or negedge rst_n)begin
    if(!rst_n)begin
        rx_start  <=  'b0;
    end else begin
        rx_start  <= ~rx_start;
    end
end
(* keep_hierarchy = "yes" *)click   rx_begin(  
                                    .in_R(rx_start),
                                    .in_A(),        
                                    .out_R(out_R_rx),   
                                    .out_A(out_A_rx),   
                                    .fire(fire_rx_begin)    
                                    );
assign inv_fire_rx_begin =  ~fire_rx_begin;

always@(posedge inv_fire_rx_begin or negedge in_R_rst)begin
    if(!in_R_rst)begin
        out_A_rx <= 'b0;
    end else begin
        out_A_rx <= out_R_rx;
    end
end                                

always@(posedge rx_data_valid or negedge rst_n)begin
    if(!rst_n)begin
        rx_final  <=  'b0;
    end else begin
        rx_final  <=  ~rx_final;
    end
end

(* keep_hierarchy = "yes" *)click rx_end(  
                                    .in_R(rx_final),
                                    .in_A(),        
                                    .out_R(out_R_rxend),   
                                    .out_A(out_A_rxend),   
                                    .fire(fire_rx_final)    
                                    );
assign inv_fire_rx_final = ~fire_rx_final;
always@(posedge inv_fire_rx_final or negedge in_R_rst)begin
    if(!in_R_rst)begin
        out_A_rxend <= 'b0;
    end else begin
        out_A_rxend <= out_R_rxend;
    end
end 

assign fire_rx_end = (sfr_addr!=`uart_rx)?fire_rx_begin:
                     (sfr_rd==1'b0)?fire_rx_begin:fire_rx_final;
assign uart_rx_in =(sfr_rd==1'b0) ? 1'b1 : uart_rx;                                    
uart_rx#
(
.CLK_FRE(CLK_FRE),
.BAUD_RATE(115200)
) uart_rx_inst
(
.clk                        (sys_clk                  ),
.rst_n                      (rst_n                    ),
.rx_data                    (rx_data                  ),
.rx_data_valid              (rx_data_valid            ),
.rx_data_ready              (rx_data_ready            ),
.rx_pin                     (uart_rx_in                  )
);
endmodule
